﻿module Perceptron

open System
open System.IO
open ScriptMonad
open MathNet.Numerics
open MathNet.Numerics.FSharp
open MathNet.Numerics.LinearAlgebra.Double
open System.Drawing
open System.Windows.Forms
open System.Diagnostics

let mi : Double = 0.1
let c = 3


type PerceptronPoint =
  struct 
    val Point   : DenseVector
    val MissCounter : int ref
    val Label   : float
    new (point, counter, label) = { Point = point; MissCounter = counter; Label = label }
  end

let inline compute_margin (label : Double) (x_i : DenseVector) (wt : DenseVector) =
    label * wt * x_i

let inline compute_w (label : Double) (x_i : DenseVector) (wt : DenseVector) =
    let margin = compute_margin label x_i wt
    if margin > 0. then true,wt
    else 
      let wt' = wt +  mi * label * x_i
      false,wt'

let inline compute_perceptron (points : PerceptronPoint list) (wt : DenseVector) =
    let wt = wt |> ref
    let rec compute_points (points : PerceptronPoint list) =
      match points with
      
      | (x::xs) when !x.MissCounter >= c ->
        compute_points xs
        
      | (x::xs) ->
        let positive_margin, wt' = compute_w x.Label x.Point !wt
        wt := wt'
        if not positive_margin then incr x.MissCounter
        (compute_points xs ) && positive_margin

      | _ -> true
        

    script{
      let watch = Stopwatch()
      do watch.Start()

#if TwoD
      let f = new Form()
      f.Width <- 800
      f.Height <- 600
      let graphics = f.CreateGraphics()
      f.Show()
      let axis_pen = new Pen(Color.Brown)
      let positive_pen = new Pen(Color.Red)
      let negative_pen = new Pen(Color.Blue)
      let w_pen = new Pen(Color.Green)
#endif

      let rec compure_rec_points guard =
        script{
          if guard then return ()
          else
#if TwoD
            do graphics.Clear(Color.White)

            //DISEGNO GLI ASSI
            do graphics.DrawLine(axis_pen, 0, 0, 300, 0)
            do graphics.DrawLine(axis_pen, 0, 0, 0, 300)

            //DISEGNO I PUNTI
            for p in points do
              let p,_,color = p
              let x = int p.[0]
              let y = int p.[1]
              do graphics.DrawRectangle((if color>0. then positive_pen else negative_pen) , Rectangle(x-2, y-2, 4, 4))

            //DISEGNO W

            let x1 = 0
            let y1 = 
              if wt.Value.[1] |> abs < 0.0000001 then 0
              else - int (wt.Value.[2] / wt.Value.[1])

            let x2 = 300
            let y2 = 
              if wt.Value.[1] |> abs < 0.0000001 then 0
              else - int (wt.Value.[0] * 300. / wt.Value.[1]) - int (wt.Value.[2] / wt.Value.[1])

            do graphics.DrawLine(w_pen, x1, y1, x2, y2)
            
            //f.Invalidate()
            f.Update()
            do! wait 0.1f
#endif
                  
            Console.WriteLine(!wt)
            return! compure_rec_points (compute_points points )
        }
      do! compure_rec_points false

      do watch.Stop()
      Console.WriteLine(watch.Elapsed)

      do! wait 10.0f
      return !wt
    }
